' Translation functions

Option Explicit

Sub Usage
  wscript.echo "Usage:"
  wscript.echo " - to translate a WiX source file"
  wscript.echo "  cscript translate.vbs transwix <sourcefile> <targetfile> <languagefile>"
  wscript.echo ""
  wscript.echo " - to generate a template file based on WiX source files"
  wscript.echo "  cscript translate.vbs genwixtempl <targetfile> <sourcefile1> <sourcefile2>..."
  wscript.echo ""
  wscript.echo " - to merge two or more template files"
  wscript.echo "  cscript translate.vbs mergetempl <targetfile> <sourcefile1> <sourcefile2>..."
  wscript.echo ""
  wscript.echo " - to generate the C source code with translation arrays"
  wscript.echo "  cscript translate.vbs gencsource <outfile> <english_file> <lang1> <lang2>..."
  wscript.echo ""
  wscript.echo " - to generate a WXI include file with radiobuttons for all languages"
  wscript.echo "  cscript translate.vbs genwxilist <outfile> <lang1> <lang2>..."
  wscript.echo ""
  wscript.echo " - to generate a file with the current status"
  wscript.echo "  cscript translate.vbs status <outfile> <lang1> <lang2>..."
  wscript.echo ""
  wscript.echo " - to generate a XML file listing all 'expired' translations"
  wscript.echo "  cscript translate.vbs listexpired <templatefile> <langfile> <outfile>"
  wscript.quit(1)
End Sub

If WScript.Arguments.Count < 1 Then TranslateWix
If WScript.Arguments(0) = "transwix" Then TranslateWix
If WScript.Arguments(0) = "genwixtempl" Then GenerateWixTemplate
If WScript.Arguments(0) = "mergetempl" Then MergeTemplates
If WScript.Arguments(0) = "gencsource" Then GenerateCSource
If WScript.Arguments(0) = "status" Then GenerateStatus
If WScript.Arguments(0) = "genwxilist" Then GenerateLanguageWxi
If WScript.Arguments(0) = "listexpired" Then GenerateExpiredList
Wscript.echo "Invalid argument: " & WScript.Arguments(0)
Usage


dim trans
dim oktrans,failtrans

Sub TranslateWix
   If WScript.Arguments.Count <> 4 Then Usage

   dim wxs : set wxs = CreateObject("Microsoft.XMLDOM")
   set trans = CreateObject("Microsoft.XMLDOM")

   wxs.Load(WScript.Arguments(1))
   trans.Load(WScript.Arguments(3))

   oktrans = 0 : failtrans = 0

   ' Translate language and codepage properties explicitly
   if wxs.documentElement.nodename = "Wix" Then
     wxs.documentElement.SelectSingleNode("Product").setAttribute "Language", _
      trans.SelectSingleNode("translation").getAttribute("language")
     wxs.documentElement.SelectSingleNode("Product").SelectSingleNode("Package").setAttribute "Languages", _
      trans.SelectSingleNode("translation").getAttribute("language")
     wxs.documentElement.SelectSingleNode("Product").setAttribute "Codepage", _
      trans.SelectSingleNode("translation").getAttribute("codepage")
     wxs.documentElement.SelectSingleNode("Product/Property[@Id='UILANG']").setAttribute "Value", _
      trans.SelectSingleNode("translation").getAttribute("language")
   end if

   ' Translate the path to the .rtf file
   dim n
   set n = wxs.SelectSingleNode("Include/Dialog[@Id='Welcome_Dialog2']/Control[@Id='welcome_rtf']/Text")
   if not n is nothing then
      n.setAttribute "SourceFile", "lang/" & trans.SelectSingleNode("translation").getAttribute("name") & "/welcome.rtf"
   end if

   dim nodeProcessor : set nodeProcessor = GetRef("TranslateString")
   ProcessNode wxs.documentElement, nodeProcessor

   wxs.Save(WScript.Arguments(2))
   wscript.echo "Translated " & oktrans & " strings, failed on " & failtrans & " strings."
   WScript.quit(0)
End Sub

Sub GenerateWixTemplate
   If WScript.Arguments.Count < 3 Then Usage

   set trans = CreateObject("Microsoft.XMLDOM")
   trans.AppendChild(trans.createProcessingInstruction("xml","version='1.0' encoding='iso-8859-1'"))
   trans.documentElement = trans.CreateNode(1,"translation","http://pginstaller.projects.postgresql.org/xmlns/translation")
   trans.documentElement.setAttribute "name","xx"
   trans.documentElement.setAttribute "englishname","xx"
   trans.documentElement.setAttribute "localname","xx"
   trans.documentElement.setAttribute "language","1033"
   trans.documentElement.setAttribute "codepage","1252"
   trans.documentElement.setAttribute "maintainer","Your name and email here"

   dim nodeProcessor : set nodeProcessor = GetRef("CollectString")
   dim i
   for i = 2 to WScript.Arguments.Count-1
      dim wxs : set wxs = CreateObject("Microsoft.XMLDOM")
      wxs.Load(WScript.Arguments(i))
      ProcessNode wxs.documentElement, nodeProcessor
   next

   trans.SelectSingleNode("translation").AppendChild(trans.createTextNode(vbCrLf))
   trans.Save(WScript.Arguments(1))

   wscript.quit(0)
End Sub

Sub MergeTemplates
   If WScript.Arguments.Count < 3 Then Usage
   dim duplicates : duplicates = 0
   dim xRoot : set xRoot = CreateObject("Microsoft.XMLDOM")

   xRoot.Load WScript.Arguments(2)
   if WScript.Arguments.Count > 3 Then
      dim i
      for i = 3 to wscript.arguments.count-1
         dim xTemp : set xTemp = CreateObject("Microsoft.XMLDOM")
         dim newnode
         xTemp.Load WScript.Arguments(i)
         for each newnode in xTemp.documentElement.childNodes

           ' Tried XPath lookup here, but it broke pretty badly. Probably because of escapes
           dim node
           dim found : found = 0
           for each node in xRoot.SelectNodes("translation/string")
              if node.getAttribute("original") = newnode.getAttribute("original") Then
                 found = 1
                 duplicates = duplicates + 1
                 exit for
              end if
           next
           if found = 0 then
             newnode.Attributes.RemoveNamedItem("caidx")
             xRoot.documentElement.AppendChild(newnode)
           end if
         next
      next
   end if
   xRoot.Save WScript.arguments(1)
   wscript.echo "Files merged, " & duplicates & " duplicated strings."
   wscript.quit(0)
End Sub

Sub GenerateCSource
   dim xml : set xml = CreateObject("msxml2.DomDocument")
   xml.async = false
   xml.Load(WScript.Arguments(2))
   dim lastidx : lastidx = -1
   dim node
   for each node in xml.SelectNodes("translation/string")
      dim idx
      idx = node.getAttribute("caidx")
      If lastidx = -1 Then
         ' Validate we start at zero!
         If not idx = 0 Then
            wscript.echo "Index does not start at zero!"
            wscript.quit(1)
         end if
      Else
         ' Validate we are in sequence!
         if not ((lastidx+1) = int(idx)) then
            wscript.echo "Index " & idx & " is not in sequence (last was " & lastidx & ")!"
            wscript.quit(1)
         end if
      end if
      lastidx = idx
   next

   dim strm, lang
   set strm = CreateObject("ADODB.Stream")
   strm.charset="UTF-8"
   strm.open
   strm.WriteText "#define WIN32_LEAN_AND_MEAN",1
   strm.WriteText "#include <windows.h>",1
   strm.WriteText "#include <stdlib.h>",1
   strm.WriteText "#include ""lang.h""",1
   strm.WriteText "",1
   strm.WriteText "const WCHAR *trans_base[] = {",1

   for each node in xml.SelectNodes("translation/string")
      strm.WriteText "  L""" & node.getAttribute("original") & """,",1
   next
   strm.WriteText "NULL};",1

   dim i
   redim languages(wscript.arguments.count)
   for i = 3 to wscript.arguments.count-1
      dim xlang : set xlang = CreateObject("msxml2.DomDocument")
      xlang.async = false
      xlang.Load wscript.arguments(i)
      languages(i-3) = xlang.documentElement.getAttribute("language")
      strm.WriteText "static const WCHAR *trans_" & languages(i-3) & "[]={",1
      for each node in xml.SelectNodes("translation/string")
         dim tnode
         dim found
         found = 0
         for each tnode in xlang.SelectNodes("translation/string")
            if node.getAttribute("original") = tnode.getAttribute("original") Then
               found = 1
               strm.WriteText "  L""" & tnode.getAttribute("translation") & """,",1
               exit for
            end if
         next
         if found = 0 then
            strm.WriteText "  NULL,",1
         end if
      next
      strm.WriteText "NULL};",1
      strm.WriteText "",1
   next

   strm.WriteText "const _transmap transmap[] = {",1
   for i = 3 to wscript.arguments.count-1
      strm.WriteText "  { " & languages(i-3) & ", trans_" & languages(i-3) & "},",1
   next
   strm.WriteText "  {-1, NULL}",1
   strm.WriteText "};",1
   strm.SaveToFile wscript.arguments(1)

   wscript.quit (0)
End Sub

Sub GenerateStatus
   dim fso : set fso = CreateObject("Scripting.FileSystemObject")
   dim f : set f = fso.CreateTextFile(WScript.Arguments(1))
   f.WriteLine("---------- Current translation status ------------")
   dim i
   for i = 2 to WScript.Arguments.Count-1
      dim xml : set xml = CreateObject("Microsoft.XMLDOM")
      xml.async = false
      xml.Load(WScript.Arguments(i) & ".xml")
      dim total, translated
      total = xml.SelectNodes("translation/string").length
      translated = xml.SelectNodes("translation/string[@translation != '']").length
      if total > 0 then f.WriteLine(WScript.Arguments(i) & ": " & total & " total strings, " & translated & " translated (" & int(translated*100/total) & "%)")
   next
   f.Close
   WScript.quit(0)
End Sub

Sub GenerateLanguageWxi
   dim strm : set strm = CreateObject("ADODB.Stream")
   strm.charset="UTF-8"
   strm.open

   strm.WriteText "<?xml version=""1.0"" encoding=""utf-8""?>",1
   strm.WriteText "<Include>",1
   strm.WriteText " <RadioButtonGroup Property=""LanguageChoice"">",1
   strm.Writetext "  <RadioButton Text=""English / English"" Value=""en"" X=""0"" Y=""0"" Height=""14"" Width=""175"" />",1
   dim y : y = 0
   dim x : x = 0
   dim i
   for i = 2 to WScript.Arguments.Count-1
      y = y +15 
      if i = 10 then 'new-line of a dialog.
        y = 0
        x = 176
      end if
      dim xml : set xml = CreateObject("Microsoft.XMLDOM")
      xml.async = false
      xml.Load(WScript.Arguments(i))
      dim ln : ln = xml.documentElement.getAttribute("name")
      dim en : en = xml.documentElement.getAttribute("englishname")
      dim lo : lo = xml.documentElement.getAttribute("localname")
      strm.WriteText "  <RadioButton Text=""" & en & " / " & lo & """ Value=""" & ln & """ X=""" & x & """ Y=""" & y & """ Height=""14"" Width=""175"" />",1
   next
   strm.WriteText " </RadioButtonGroup>",1
   strm.WriteText "</Include>",1
   strm.SaveToFile wscript.arguments(1)
   WScript.quit(0)
End Sub

Sub GenerateExpiredList
   dim en : set en = CreateObject("Microsoft.XMLDOM")
   dim ln : set ln = CreateObject("Microsoft.XMLDOM")
   dim ox : set ox = CreateObject("Microsoft.XMLDOM")

   en.async = false : ln.async = false

   en.Load(WScript.Arguments(1))
   ln.Load(WScript.Arguments(2))

   ox.documentElement = ox.CreateElement("expired")

   dim node
   for each node in ln.SelectNodes("translation/string")
     dim enode
     dim found : found = 0
     for each enode in en.SelectNodes("translation/string")
        if enode.getAttribute("original") = node.getAttribute("original") then
           found = 1
           exit for
        end if
     next
     if found = 0 then
        ox.documentElement.AppendChild node
     end if
   next
   if WScript.Arguments(3) = "-" then
      wscript.echo ox.xml
   else
      ox.Save WScript.Arguments(3)
   end if
   wscript.echo "Done."
   wscript.quit(0)
End Sub

Sub ProcessNode(node, nodeProcessor)
   dim attrib
   dim subNode

   ' Scan for attributes
   for each attrib in node.attributes
      ' Don't translate Text node in special case
      If attrib.name="Text" And _
         node.nodename="Control" And _
         (node.getAttribute("Type") = "Icon" Or node.getAttribute("Type") = "Bitmap" Or (node.getAttribute("Type")="PushButton" And node.getAttribute("Icon")="yes")) _
         Then
         ' Ignore
      ' Generic attributes to always translate
      ElseIf attrib.name = "Description"  Or _
         attrib.name = "Title" Or _
         attrib.name = "Text" Or _
         attrib.name = "ToolTip" _
         Then attrib.text = nodeProcessor(attrib.text)

      ' Element specific translations
      ElseIf node.nodename = "Shortcut" Then
         If attrib.name = "Name" Then attrib.text = nodeProcessor(attrib.text)

      ElseIf node.nodename = "Directory" And Left(node.getAttribute("Id"),2) = "PM" And Not node.getAttribute("Id") = "PMDir" Then
         If attrib.name = "Name" Then attrib.text = nodeProcessor(attrib.text)
      End If
   next

   ' Translate all childnodes
   if node.childNodes.Length > 0 Then
      for each subNode in node.childNodes
         if subNode.nodetype = 1 then
            'NODE_ELEMENT
            ProcessNode subNode,nodeProcessor
         end if
         if subNode.nodetype = 3 then
            'NODE_TEXT
            if node.nodename = "ProgressText" _
            or node.nodename = "Text" _
            or node.nodename = "UIText" _
            Then node.text = nodeProcessor(node.text)
         end if
      next
   End If
End Sub

Function TranslateString(s)
   if s="" then
      TranslateString = ""
      Exit Function
   End If

   ' Tried XPath lookup here, but it broke pretty badly. Probably because of escapes
   dim node
   for each node in trans.SelectNodes("translation/string")
      if node.getAttribute("original") = s Then
         dim x
         x = node.getAttribute("translation")
         if x = "" then
            failtrans = failtrans + 1
            TranslateString = s
         else
            oktrans = oktrans + 1
            TranslateString = x
         end if
         Exit Function
      end if
   next
   failtrans = failtrans + 1
   TranslateString = s
End Function

Function CollectString(s)
   CollectString = s
   dim x
   set x = trans.SelectSingleNode("translation/string[@original='" + EscapeXpath(s) + "']")
   if x is nothing then
      dim y
      set y = trans.CreateNode(1,"string","http://pginstaller.projects.postgresql.org/xmlns/translation")
      y.setAttribute "original",s
      y.setAttribute "translation",""
      trans.SelectSingleNode("translation").AppendChild(y)
      trans.SelectSingleNode("translation").insertBefore trans.createTextNode(vbCrLf & "  "),y
   end if
end function

Function EscapeXpath(ins)
   dim s
   s = Replace(ins,"'","\'")
   EscapeXpath = s
End Function


